package org.dreamwork.assistant.tools.cli;

import org.dreamwork.util.StringUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.*;

/**
 * Created by seth.yang on 2018/10/12
 */
public class XMLReader {
    private static final DocumentBuilder builder;
    static {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance ();
            builder = factory.newDocumentBuilder ();
        } catch (Exception ex) {
            throw new RuntimeException (ex);
        }
    }

    static final class TreeNode {
        String name, value;
        List<TreeNode> children;
        TreeNode parent;
        int deep = 0;

        TreeNode () {}

        TreeNode (String name, String value) {
            this.name  = name;
            this.value = value;
        }

        void appendChild (TreeNode node) {
            if (children == null) {
                children = new ArrayList<> ();
            }

            children.add (node);
            node.parent = this;
            node.deep = deep + 1;
        }
    }

//    private Map<String, Object> flat = new HashMap<> ();
    private Stack<TreeNode> stack = new Stack<> ();
    private TreeNode root = new TreeNode ();

    public XMLReader (InputStream in) throws Exception {
        Document doc = builder.parse (in);
        stack.push (root);
        walk (doc.getDocumentElement (), root);
    }

    public Object getValue (TreeNode root, String path) {
        if (StringUtil.isEmpty (path)) {
            return null;
        }

        if (root.children == null || root.children.isEmpty ()) {
            return null;
        }

        path = path.trim ();
        if (path.charAt (0) == '/') {
            path = path.substring (1);
        }

/*
        String index = path.replace ('.', '/');
        index = buildPath (root, index);
        if (flat.containsKey (index)) {
            return flat.get (index);
        }
*/

        String[] parts = path.split ("[/]");
        TreeNode c = root;
        int i = 0;
        for (; i < parts.length; i ++) {
            String name = parts [i];
            boolean found = false;
            for (TreeNode node : c.children) {
                if (name.equals (node.name)) {
                    c = node;
                    found = true;
                    break;
                }
            }

            if (!found) {
                return null;
            }
        }

        if (c != root) {
//            if (i == parts.length) {
                String value = c.value;
                if (!StringUtil.isEmpty (value)) {
                    value = value.trim ();
//                    flat.put (index, value);
                    return value;
                } else {
                    return c.children;
                }
/*
            } else {
                return c.children;
            }
*/
        }

        return null;
    }

    public Object getValue (String path) {
        return getValue (root, path);
    }

    private void walk (Element parent, TreeNode node) {
        NodeList list = parent.getChildNodes ();
        for (int i = 0; i < list.getLength (); i ++) {
            Node n = list.item (i);
            if (n.getNodeType () == Node.ELEMENT_NODE) {
                TreeNode tn = new TreeNode (n.getNodeName (), null);
                node.appendChild (tn);
                push (tn);
                walk ((Element) n, tn);
                pop ();
            } else if (n.getNodeType () == Node.TEXT_NODE) {
                TreeNode tn = peek ();
                if (tn != null) {
                    String value = n.getNodeValue ();
                    if (!StringUtil.isEmpty (value))
                        tn.value = n.getNodeValue ();
                }
            }
        }
    }

    private void push (TreeNode node) {
        stack.push (node);
    }

    private void pop () {
        stack.pop ();
    }

    private TreeNode peek () {
        return stack.peek ();
    }

/*
    private String currentPath () {
        if (stack.isEmpty ()) {
            return null;
        }

        StringBuilder builder = new StringBuilder ();
        int pos = 0;
        for (TreeNode e : stack) {
            if (pos != 0) {
                builder.append ('/');
            }
            builder.append (e.name);
            pos ++;
        }
        return builder.toString ();
    }
*/

/*
    private String buildPath (TreeNode node, String name) {
        if (node == root) {
            return name;
        }
        String[] parts = new String[node.deep];
        int pos = node.deep - 1;
        TreeNode n = node;
        while (n != null && n != root) {
            parts [pos --] = n.name;
            n = n.parent;
        }
        StringBuilder builder = new StringBuilder ();
        for (String p : parts) {
            if (builder.length () > 0) {
                builder.append ('/');
            }
            builder.append (p);
        }
        return builder.append ('/').append (name).toString ();
    }
*/

    @SuppressWarnings ("all")
    public static void main (String[] args) throws Exception {
        XMLReader reader;
        try (InputStream in = new FileInputStream ("D:\\workspace\\dreamwork\\modules\\assistant\\pom.xml")) {
            reader = new XMLReader (in);
        }

        List<TreeNode> deps = (List<TreeNode>) reader.getValue ("dependencies");
        if (deps != null) {
            for (TreeNode node : deps) {
/*
                String groupId = node.children.get (0).value;
                String artifactId = node.children.get (1).value;
                String version = node.children.get (2).value;
*/
                String groupId = (String) reader.getValue (node, "groupId");
                String artifactId = (String) reader.getValue (node, "artifactId");
                String version = (String) reader.getValue (node, "version");

                System.out.printf ("%s:%s:%s%n", groupId, artifactId, version);
            }
        }

        Object o = reader.getValue ("build.resources");
        System.out.println (o == null ? "<null>" : o.getClass ().getName ());
    }
}